package com.hjy.json;

import javax.swing.*;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

public class Json implements Serializable {
    public Type type;
    public Value value;

    public Json() {
        this.type = Type.json_null;
        this.value = new Value();
    }

    public Json(Type value) {
        this.type = value;
        switch (value) {
            case json_int:
                this.value = new Value();
                this.value.m_int = 0;
                break;
            case json_bool:
                this.value = new Value();
                this.value.m_bool = Boolean.FALSE;
                break;
            case json_null:
                this.value = new Value();
                break;
            case json_array:
                this.value = new Value();
                this.value.m_array = new ArrayList<>();
                break;
            case json_double:
                this.value = new Value();
                this.value.m_double = 0.0;
                break;
            case json_object:
                this.value = new Value();
                this.value.m_object = new HashMap<>();
                break;
            case json_string:
                this.value = new Value();
                this.value.m_string = "";
                break;
            default:
                break;
        }

    }

    public Json(Integer value) {
        this.type = Type.json_int;
        this.value = new Value();
        this.value.m_int = value;
    }

    public Json(Boolean value) {
        this.type = Type.json_bool;
        this.value = new Value();
        this.value.m_bool = value;
    }

    public Json(Double value) {
        this.type = Type.json_double;
        this.value = new Value();
        this.value.m_double = value;
    }

    public Json(String value) {
        this.type = Type.json_string;
        this.value = new Value();
        this.value.m_string = value;
    }

    public Json(List<Json> value) {
        this.type = Type.json_array;
        this.value = new Value();
        // 深拷贝一下
        ArrayList<Json> list = new ArrayList<>();
        for (Json json : value) {
            try {
                list.add(json.deepClone());
            } catch (IOException | ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        }
        this.value.m_array = list;
    }

    public Json(Map<String, Json> value) {
        this.type = Type.json_object;
        this.value = new Value();
        // 深拷贝一下
        HashMap<String, Json> map = new HashMap<>();
        for (String s : value.keySet()) {
            try {
                map.put(s, value.get(s).deepClone());
            } catch (IOException | ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        }
        this.value.m_object = map;
    }

    public enum Type implements Serializable {
        json_null(0),
        json_bool(1),
        json_int(2),
        json_double(3),
        json_string(4),
        json_array(5),
        json_object(6);

        public final Integer type;

        Type(int type) {
            this.type = type;
        }
    }

    private static class Value implements Serializable {
        public Boolean m_bool;
        public Integer m_int;
        public Double m_double;
        public String m_string;
        public List<Json> m_array;
        public Map<String, Json> m_object;
    }

    public Class getTypeClass() {
        if (type == Type.json_bool) {
            return Boolean.class;
        }else if (type == Type.json_int) {
            return Integer.class;
        }else if (type == Type.json_double) {
            return Double.class;
        }else if (type == Type.json_string) {
            return String.class;
        }else if (type == Type.json_array) {
            return List.class;
        }else if (type == Type.json_object) {
            return Map.class;
        }else {
            return null;
        }
    }
    /**
     * 深拷贝
     *
     * @return 克隆的对象
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public Json deepClone() throws IOException, ClassNotFoundException {
        //将对象写入流中
        //ByteArrayOutputStream : 这个类实现了一个输出流的数据写入一个字节数组(字节数组在其内部)
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
        objectOutputStream.writeObject(this);
        //从流中取出
        // ByteArrayInputStream : 这个类可以读取传入的字节数组
        ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
        ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
        return (Json) objectInputStream.readObject();
    }


    @Override
    public String toString() {
        StringBuffer buffer = new StringBuffer();
        switch (type) {
            case json_null:
                buffer.append("null");
                break;
            case json_string:
                buffer.append('"');
                buffer.append(value.m_string);
                buffer.append('"');
                break;
            case json_object:
                buffer.append('{');
                AtomicInteger idx = new AtomicInteger();
                value.m_object.forEach((s, json) -> {
                    if (idx.get() != 0) {
                        buffer.append(',');
                    }
                    buffer.append("\"").append(s).append("\":");
                    buffer.append(json.toString());
                    idx.getAndIncrement();
                });
                buffer.append('}');
                break;
            case json_double:
                buffer.append(value.m_double);
                break;
            case json_array:
                buffer.append('[');
                for (int i = 0; i < value.m_array.size(); i++) {
                    if (i != 0) {
                        buffer.append(',');
                    }
                    buffer.append(value.m_array.get(i).toString());
                }
                buffer.append(']');

                break;
            case json_bool:
                if (value.m_bool) {
                    buffer.append("true");
                } else {
                    buffer.append("false");
                }
                break;
            case json_int:
                buffer.append(value.m_int);
                break;
        }

        return buffer.toString();
    }

    /**
     * 将一个 json字符串转换为 Json 对象
     *
     * @param json json 字符串
     * @return Json 对象
     */
    public static Json parseStringAsJson(String json) {
        return new Parser(json).Parse();
    }

    /**
     * 将一个对象转换为 Json 对象
     *
     * @param
     * @return
     */
    public static Json parseObjectAsJson(Object obj) throws IllegalAccessException, NoSuchFieldException {
        if (obj instanceof List) {
            List list = (List) obj;
            ArrayList<Json> jsons = new ArrayList<>();
            for (Object o : list) {
                jsons.add(Json.parseObjectAsJson(o));
            }
            return new Json(jsons);
        } else if (obj instanceof Integer) {
            return new Json((Integer) obj);
        } else if (obj instanceof Double) {
            return new Json((Double) obj);
        } else if (obj instanceof Boolean) {
            return new Json((Boolean) obj);
        } else if (obj instanceof String) {
            return new Json((String) obj);
        } else if (obj instanceof Map) {
            HashMap<String, Json> jsonMap = new HashMap<>();
            Map map = (Map) obj;
            for (Object o : map.keySet()) {
                if (o instanceof String) {
//                    json.value.m_object.put(Json.parseObjectAsJson(o).toString(), Json.parseObjectAsJson(map.get(o)));
                    jsonMap.put((String) o, Json.parseObjectAsJson(map.get(o)));
                } else {
                    throw new RuntimeException("map 对象的key必须是 String 类型");
                }
            }
            return new Json(jsonMap);
        }


        Field[] declaredFields = obj.getClass().getDeclaredFields();
        HashMap<String, Json> map = new HashMap<>();
        for (int i = 0; i < declaredFields.length; i++) {
            declaredFields[i].setAccessible(true);
            String key = declaredFields[i].getName();
            Object value = declaredFields[i].get(obj);
            map.put(key, Json.parseObjectAsJson(value));
        }
        return new Json(map);
    }


    private static Boolean checkTypeEqual(Class beanClass, String type) {
        if (type.equals("string")) {
            return String.class == beanClass;
        }else if (type.equals("int")) {
            return Integer.class == beanClass || int.class == beanClass;
        }else if (type.equals("double")) {
            return Double.class == beanClass || double.class == beanClass;
        }else if (type.equals("bool")) {
            return Boolean.class == beanClass || boolean.class == beanClass;
        }else if (type.equals("list")) {
            // beanClass 是否是 Map 的继承
            return List.class.isAssignableFrom(beanClass);
        }else if (type.equals("map")) {
            return Map.class.isAssignableFrom(beanClass);
        }

        return false;
    }

    /**
     * 将 json 字符串转换成 Bean
     *
     * @param jsonString json 字符串
     * @param beanClass  转换后的bean类型
     * @param <T>
     * @return 转换后的bean
     */
    public static <T> T toBean(String jsonString, Class<T> beanClass) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Json json = Json.parseStringAsJson(jsonString);

        // 是数组 字符串 数字
        if (Json.checkTypeEqual(beanClass, "list")) {
            ArrayList<Object> objects = new ArrayList<>();
            for (Json json1 : json.value.m_array) {
                objects.add(Json.toBean(json1.toString(),json1.getTypeClass()));
            }
            return (T)objects;
        } else if (Json.checkTypeEqual(beanClass, "int")) {
            return (T) Integer.valueOf(Integer.parseInt(jsonString));
        } else if (Json.checkTypeEqual(beanClass, "double")) {
            return (T) Double.valueOf(Double.parseDouble(jsonString));
        } else if (Json.checkTypeEqual(beanClass, "bool")) {
            return (T) Boolean.valueOf(Boolean.parseBoolean(jsonString));
        } else if (Json.checkTypeEqual(beanClass, "string")) {
            return (T) jsonString;
        } else if (Json.checkTypeEqual(beanClass, "map")) {
            HashMap<String, Object> hashMap = new HashMap<>();
            for (String s : json.value.m_object.keySet()) {
                Json json1 = json.value.m_object.get(s);
                hashMap.put(s,Json.toBean(json1.toString(),json1.getTypeClass()));
            }
            return (T)hashMap;
        }

        // 获取无参构造方法
        // int.class == beanClass || Integer.class == beanClass
        Constructor<T> constructor = beanClass.getConstructor();
        constructor.setAccessible(true);
        T instance = constructor.newInstance();
        Field[] declaredFields = beanClass.getDeclaredFields();
        for (String name : json.value.m_object.keySet()) {
            for (Field declaredField : declaredFields) {
                if (declaredField.getName().equals(name)) {
                    declaredField.setAccessible(true);
                    // TODO 赋值
                    declaredField.set(instance, Json.toBean(json.value.m_object.get(name).toString(), declaredField.getType()));
                    break;
                }
            }
        }

        return instance;
    }

    // object getKey
    // array getIdx
    public Json getKey(String key) {
        if (type == Type.json_array) {
            throw new RuntimeException("无法在数组中使用key获取值, 请使用 getIdx(idx) ");
        }
        if (type == Type.json_object) {
            return value.m_object.get(key);
        } else {
            return this;
        }
    }

    public Json getIdx(Integer idx) {
        if (type == Type.json_object) {
            throw new RuntimeException("无法在对象中使用idx获取值, 请使用 getKey(key) ");
        }
        if (type == Type.json_array) {
            return value.m_array.get(idx);
        } else {
            return this;
        }
    }

    public void setKey(String key, Integer val) {
        if (type == Type.json_array) {
            throw new RuntimeException("无法在数组中使用key设置值, 请使用 setIdx(idx) ");
        }
        if (type == Type.json_object) {
            value.m_object.put(key, new Json(val));
        }
    }

    public void setKey(String key, Double val) {
        if (type == Type.json_array) {
            throw new RuntimeException("无法在数组中使用key设置值, 请使用 setIdx(idx) ");
        }
        if (type == Type.json_object) {
            value.m_object.put(key, new Json(val));
        }
    }

    public void setKey(String key, Boolean val) {
        if (type == Type.json_array) {
            throw new RuntimeException("无法在数组中使用key设置值, 请使用 setIdx(idx) ");
        }
        if (type == Type.json_object) {
            value.m_object.put(key, new Json(val));
        }
    }

    public void setKey(String key) {
        if (type == Type.json_array) {
            throw new RuntimeException("无法在数组中使用key设置值, 请使用 setIdx(idx) ");
        }
        if (type == Type.json_object) {
            value.m_object.put(key, new Json());
        }
    }

    public void setKey(String key, String val) {
        if (type == Type.json_array) {
            throw new RuntimeException("无法在数组中使用key设置值, 请使用 setIdx(idx) ");
        }
        if (type == Type.json_object) {
            value.m_object.put(key, new Json(val));
        }
    }


    public void setIdx(Integer idx, Boolean val) {
        if (type == Type.json_object) {
            throw new RuntimeException("无法在对象中使用idx设置值, 请使用 setKey(key) ");
        }
        if (type == Type.json_array) {
            value.m_array.set(idx, new Json(val));
        }
    }

    public void setIdx(Integer idx, Integer val) {
        if (type == Type.json_object) {
            throw new RuntimeException("无法在对象中使用idx设置值, 请使用 setKey(key) ");
        }
        if (type == Type.json_array) {
            value.m_array.set(idx, new Json(val));
        }
    }

    public void setIdx(Integer idx, Double val) {
        if (type == Type.json_object) {
            throw new RuntimeException("无法在对象中使用idx设置值, 请使用 setKey(key) ");
        }
        if (type == Type.json_array) {
            value.m_array.set(idx, new Json(val));
        }
    }

    public void setIdx(Integer idx) {
        if (type == Type.json_object) {
            throw new RuntimeException("无法在对象中使用idx设置值, 请使用 setKey(key) ");
        }
        if (type == Type.json_array) {
            value.m_array.set(idx, new Json());
        }
    }

    public void setIdx(Integer idx, String val) {
        if (type == Type.json_object) {
            throw new RuntimeException("无法在对象中使用idx设置值, 请使用 setKey(key) ");
        }
        if (type == Type.json_array) {
            value.m_array.set(idx, new Json(val));
        }
    }

}
